#include "common_Socket.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>


/* ****************************************************************** */
/*                               SOCKET                               */
/* ****************************************************************** */


Socket::Socket(){}
Socket::Socket(int socket):fd(socket){}
Socket::Socket(const Socket & s):fd(s.fd){}

// Envia el string pasado por parametro. El string enviado se corresponde
// 1 a 1 con un string recibido del otro lado del socket.
// Si se envio exitosamente el string, retorna 0. En caso de que
// se desconecte del cliente, retorna != 0.
int Socket::sendS(const string & enviado){

	size_t n = enviado.size(), nEnviado = 0, nAux;
	int j = send(fd, &n, sizeof(size_t), 0);
	if (j < 0) return -1;

	const char * buffer = enviado.c_str();

	while (nEnviado < n){
		nAux = send(fd, buffer + nEnviado, n - nEnviado, 0);
		if (nAux < 0) return -1;
		else nEnviado += nAux;
	};
	return 0;
}

// Recibe un string. El string recibido se corresponde 1 a 1 con
// un string enviado del otro lado del socket.
// Si se recibio exitosamente el string, retorna 0. En caso de que
// se desconecte del cliente, retorna != 0.
int Socket::receiveS(string & recibido){

	size_t n;
	int j = read(fd, &n, sizeof(size_t));
	if (j < 0) return -1;

	recibido.clear();
	char buffer[256];
	memset(buffer, 0, 256);
	size_t nLeido = 0, nAux;

	while (nLeido < n){
		int p = ((256 < (n - nLeido)) ? 255 : n - nLeido);
		nAux = read(fd, buffer, p);
		if (nAux < 0) return -1;
		else nLeido += nAux;
		buffer[p] = '\0';
		recibido += buffer;
	};
	return 0;
}

// Cierra el socket.
void Socket::closeS(){
	shutdown(fd, 2);
	close(fd);
}

/* ****************************************************************** */
/*                           SERVER SOCKET                            */
/* ****************************************************************** */


ServerSocket::ServerSocket():Socket(){}


// Prepara al socket para aceptar conexiones.
// Si el socket queda listo para aceptar conexiones, retorna 0,
// en caso contrario retorna != 0.
int ServerSocket::listenS(int puerto){

	struct sockaddr_in direccionServer;

	fd = socket(AF_INET, SOCK_STREAM, 0);

	if (fd == -1) return -1;
	memset((char *) &direccionServer, 0, sizeof(direccionServer));

	direccionServer.sin_family = AF_INET;
	direccionServer.sin_addr.s_addr = INADDR_ANY;
	direccionServer.sin_port = htons(puerto);

	int estado = bind(fd, (struct sockaddr *) &direccionServer,
				sizeof(direccionServer));

	if (estado == -1) return -1;
	listen(fd,5);

	return 0;
}

// Acepta una conexion. Crea un nuevo socketCliente que manejara
// el intercambio de informacion con el cliente.
// Si la conexion se establece exitosamente, retorna 0, en caso
// contrario retorna != 0.
int ServerSocket::acceptS(Socket & socketCliente){

	struct sockaddr_in direccionCliente;

	socklen_t longitud = sizeof(direccionCliente);
	int nuevoSocket = accept(fd, (struct sockaddr *) &direccionCliente, &longitud);
	if (nuevoSocket == -1) return -1;
	else {
		socketCliente = Socket(nuevoSocket);
		return 0;
	};
}


/* ****************************************************************** */
/*                           CLIENT SOCKET                            */
/* ****************************************************************** */

ClientSocket::ClientSocket():Socket(){}


// Conecta el socket al IP y puerto proporcionados.
// Si la conexion tiene exito, retorna 0, en caso contrario retorna != 0.
int ClientSocket::connectS(string ip, int puerto){

	struct sockaddr_in direccionServer;

	this->fd = socket(AF_INET, SOCK_STREAM, 0);
	if (fd < 0) return -1;

	memset((char *) &direccionServer, 0, sizeof(direccionServer));
	direccionServer.sin_family = AF_INET;
	direccionServer.sin_addr.s_addr = inet_addr(ip.c_str());
	direccionServer.sin_port = htons(puerto);

	int i = connect(fd, (struct sockaddr *) &direccionServer, sizeof(direccionServer));
	if (i < 0) return -1;

	return 0;
}

